home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Skunkware 5
/
Skunkware 5.iso
/
src
/
X11
/
xconq
/
map.c
< prev
next >
Wrap
C/C++ Source or Header
|
1995-05-09
|
29KB
|
1,090 lines
/* Copyright (c) 1987, 1988 Stanley T. Shebs, University of Utah. */
/* This program may be used, copied, modified, and redistributed freely */
/* for noncommercial purposes, so long as this notice remains intact. */
#pragma comment(exestr, "@(#) map.c 12.1 95/05/09 ")
/* RCS $Header: map.c,v 1.4 88/07/20 14:29:55 shebs Exp $ */
/* Code relating to the reading and writing of mapfiles. A mapfile comes */
/* in several independent sections, each of whose presence is flagged in the */
/* header line. A mapfile may also specify the inclusion of other mapfiles. */
#include "config.h"
#include "misc.h"
#include "dir.h"
#include "period.h"
#include "side.h"
#include "unit.h"
#include "map.h"
#include "global.h"
/* A mapfile header has various pieces that we need to save. */
typedef struct a_header {
int numincls; /* number of included files */
char *includes[MAXINCLUDES]; /* raw names of included files */
int numnotes; /* number of lines in designer notes */
char *notes[MAXMAPNOTES]; /* designer notes for mapfile */
char *sections; /* +/- string of mapfile sections */
int sdetail;
int udetail;
} Header;
/* Sections of a mapfile have their recognizers. */
#define vsect(s) (s[0] == '+')
#define psect(s) (s[1] == '+')
#define msect(s) (s[2] == '+')
#define gsect(s) (s[3] == '+')
#define ssect(s) (s[4] == '+')
#define usect(s) (s[5] == '+')
/* These vars tell init code what it has to do itself. */
extern bool periodmade, mapmade, globalsmade, sidesmade, unitsmade;
extern int nextid;
World world; /* the world structure itself! */
Header *mainhead = NULL; /* the "top-level" map header - needs saving */
int nummaps; /* Number of mapfiles loaded */
int curgiven; /* tmp for side creation */
int nux, nuy; /* where to put a newly created unit */
int nusn; /* Number of unit's side */
int nut; /* Number of the Unit Transport */
char *mapfilenames[MAXLOADED]; /* Names of loaded files */
char *perfilename = NULL;
bool sidecountsread; /* Obscure, fixes numbers of restored units */
/* Malloc just enough space for the map. */
allocate_map()
{
world.terrain = (Hex *) malloc(world.width * world.height * sizeof(Hex));
}
/* Game files can live in library directories or somewhere else. This */
/* function tries to find a file, open it, and load the contents. */
load_mapfile(name)
char *name;
{
bool loaded = FALSE;
int i;
FILE *fp = NULL;
make_pathname(XCONQLIB, name, "", spbuf);
for (i = 0; i < nummaps; ++i) {
if (strcmp(name, mapfilenames[i]) == 0) { loaded = TRUE; break; }
if (strcmp(spbuf, mapfilenames[i]) == 0) { loaded = TRUE; break; }
}
if (loaded) {
fprintf(stderr, "\"%s\" is already loaded.\n", name);
} else if ((fp = fopen(spbuf, "r")) != NULL) {
if (Debug) printf("Reading \"%s\" ...\n", spbuf);
mapfilenames[nummaps++] = copy_string(spbuf);
} else if ((fp = fopen(name, "r")) != NULL) {
if (Debug) printf("Reading \"%s\" ...\n", name);
mapfilenames[nummaps++] = copy_string(name);
} else {
fprintf(stderr, "Neither \"%s\" or \"%s\" could be opened!\n",
spbuf, name);
exit(1);
}
if (fp != NULL) {
load_sections(fp);
fclose(fp);
}
if (periodmade) {
perfilename = name;
periodmade = FALSE;
}
}
/* Grab up all the components of a mapfile, and recurse if any submapfiles. */
/* The header line tells us which sections to try to load. For some */
/* sections, flag them as loaded so we know not to synthesize replacements. */
load_sections(fp)
FILE *fp;
{
char sect[BUFSIZE], *tmp;
int numfiles, i;
Header *head;
/* Read and interpret the header */
head = (Header *) malloc(sizeof(Header));
if (mainhead == NULL) mainhead = head;
fscanf(fp, "Xconq %d %s", &numfiles, sect);
head->numincls = numfiles;
head->sections = copy_string(sect);
head->sdetail = head->udetail = 0;
tmp = read_line(fp);
head->notes[0] = tmp;
head->numnotes = 1;
if (tmp[strlen(tmp)-1] == ';') {
for (i = 1; i < MAXMAPNOTES; ++i) {
head->notes[i] = read_line(fp);
if (strcmp(head->notes[i], ".") == 0) {
head->numnotes = i;
break;
}
if (Debug) printf("%s\n", head->notes[i]);
}
}
for (i = 0; i < numfiles; ++i) {
head->includes[i] = read_line(fp);
if (Debug) printf("Including %s ...\n", head->includes[i]);
load_mapfile(head->includes[i]);
}
sidecountsread = FALSE;
/* Suck in all the sections */
if (vsect(sect)) {
read_version(fp);
}
if (psect(sect)) {
read_period(fp);
periodmade = TRUE;
}
if (msect(sect)) {
read_map(fp);
mapmade = TRUE;
}
if (gsect(sect)) {
read_globals(fp);
globalsmade = TRUE;
}
if (ssect(sect)) {
head->sdetail = read_sides(fp);
sidesmade = TRUE;
}
if (usect(sect)) {
head->udetail = read_units(fp);
unitsmade = TRUE;
}
sidecountsread = FALSE;
}
/* Read the version line and test it against the program version. */
/* Continuation after failure is likely to result in core dump, so leave. */
read_version(fp)
FILE *fp;
{
char *tmpver;
if (Debug) printf("Will try to read version ...\n");
tmpver = read_line(fp);
if (strcmp(tmpver, version) != 0) {
fprintf(stderr, "Sorry, mapfile is for some other version!\n");
fprintf(stderr, "Version should be \"%s\", not \"%s\".\n",
tmpver, version);
exit(1);
}
if (Debug) printf("... Done reading version.\n");
}
/* Read the map section, starting with header, then working through data. */
/* The map data may be partly run-length encoded; this is recognized by the */
/* presence of digit strings (of arbitrary length) followed by the run char. */
/* Any or all of the data may be so encoded, otherwise the chars are 1-1 */
/* with map hexes; newlines are always at the end of each map row. */
read_map(fp)
FILE *fp;
{
char ch;
int extension, x, y, terr, run;
fscanf(fp, "Map %d %d %d %d %d\n",
&world.width, &world.height,
&world.scale, &world.known, &extension);
if (Debug) printf("Will try to read %dx%d map ...\n",
world.width, world.height);
allocate_map();
for (y = world.height-1; y >= 0; --y) {
for (x = 0; x < world.width; /* incr below */) {
ch = getc(fp);
if (isdigit(ch)) {
run = ch - '0';
while (isdigit(ch = getc(fp))) {
run = run * 10 + ch - '0';
}
} else {
run = 1;
}
terr = find_terr(ch);
if (terr < 0) terr = period.defaultterrain;
if (terr < 0) {
fprintf(stderr, "'%c' is not valid terrain in this period!\n",
ch);
exit(1);
}
while (run-- > 0) {
set_terrain_at(x, y, terr);
set_people_at(x, y, NOBODY);
set_unit_at(x, y, NULL);
++x;
}
}
fscanf(fp, "\n");
}
if (Debug) printf("... Done reading map.\n");
}
/* This should be more efficient. Fortunately, run-length encoded maps */
/* don't call it for every single hex! */
find_terr(ch)
char ch;
{
register int t;
for_all_terrain_types(t) if (ch == ttypes[t].tchar) return t;
return (-1);
}
/* Period reading is complicated, and therefore lives in its own file. */
/* Read the globals section. Most info of interest is in the header, but */
/* win/lose conditions need extra reads. */
read_globals(fp)
FILE *fp;
{
int extension, i, u, r;
Condition *cond;
fscanf(fp, "Globals %d %d %d %d %d %d\n",
&global.time, &global.endtime, &global.setproduct,
&global.leavemap, &global.numconds, &extension);
if (Debug) printf("Will try to read globals ...\n");
for (i = 0; i < global.numconds; ++i) {
cond = &(global.winlose[i]);
fscanf(fp, "%d %d %d %d %d\n",
&(cond->win), &(cond->type),
&(cond->starttime), &(cond->endtime), &(cond->sidesn));
switch (cond->type) {
case TERRITORY:
fscanf(fp, "%d\n", &(cond->n));
break;
case UNITCOUNT:
for_all_unit_types(u) {
fscanf(fp, "%d", &(cond->units[u]));
}
fscanf(fp, "\n");
break;
case RESOURCECOUNT:
for_all_resource_types(r) {
fscanf(fp, "%d", &(cond->resources[r]));
}
fscanf(fp, "\n");
break;
case POSSESSION:
fscanf(fp, "%d %d %d\n", &(cond->x), &(cond->y), &(cond->n));
break;
default:
case_panic("condition type", cond->type);
break;
}
}
if (Debug) printf("... Done reading globals.\n");
}
/* Read in the entire sides section. Sides have several levels of detail, */
/* since sides' views of the world can be pretty large and we don't always */
/* need to remember them. */
int sidedetail;
read_sides(fp)
FILE *fp;
{
int numtoread, detail, extension, i;
Side *side;
curgiven = 0;
fscanf(fp, "Sides %d %d %d\n", &numtoread, &detail, &extension);
if (detail < 0 || detail > SIDEALL) case_panic("detail", detail);
sidedetail = detail;
if (Debug) printf("Will try to read %d sides at detail %d ...\n",
numtoread, detail);
for (i = 0; i < numtoread; ++i) {
if (detail >= SIDEBASIC) side = read_basic_side(fp);
if (side != NULL) {
if (detail >= SIDESLOTS) read_side_attributes(side, numtoread, fp);
if (detail >= SIDEVIEW) read_side_view(side, fp);
if (detail >= SIDEMISC) {
read_side_misc(side, fp);
read_side_statistics(side, fp);
}
if (Debug) printf("Got side named \"%s\",\n", side->name);
}
}
if (Debug) printf("... Done reading sides.\n");
return detail;
}
/* Create a side, which only requires a name - person-ness and display come */
/* from the command line if any were supplied. */
/* Somebody should react coherently to side creation failure... */
Side *
read_basic_side(fp)
FILE *fp;
{
bool human = FALSE;
char *host = NULL;
int j;
fscanf(fp, "%s\n", tmpbuf);
for (j = 0; j < strlen(tmpbuf); ++j) if (tmpbuf[j] == '*') tmpbuf[j] = ' ';
if (curgiven < numgivens) {
human = humans[curgiven];
host = hosts[curgiven];
++curgiven;
}
return create_side(tmpbuf, human, host);
}
/* Read the most important attributes of a side. */
read_side_attributes(side, numtoread, fp)
Side *side;
int numtoread;
FILE *fp;
{
int s, u;
fscanf(fp, "%hd", &(side->ideology));
for (s = 0; s < numtoread; ++s) fscanf(fp, "%hd", &(side->attitude[s]));
for_all_unit_types(u) fscanf(fp, "%hd", &(side->counts[u]));
fscanf(fp, "\n");
sidecountsread = TRUE;
}
/* Read the goriest of details about a side - those things that are */
/* relevant only to a particular game. */
read_side_misc(side, fp)
Side *side;
FILE *fp;
{
bool human;
char host[BUFSIZE];
fscanf(fp, "%d %s %hd ",
&human, host, &(side->lost));
fscanf(fp, "\n");
}
/* Read the performance statistics of a side. */
read_side_statistics(side, fp)
Side *side;
FILE *fp;
{
int u, u2, i;
for_all_unit_types(u) {
for (i = 0; i < NUMREASONS; ++i) {
fscanf(fp, "%hd", &(side->balance[u][i]));
}
fscanf(fp, "\n");
for_all_unit_types(u2) {
fscanf(fp, "%hd", &(side->atkstats[u][u2]));
}
fscanf(fp, "\n");
for_all_unit_types(u2) {
fscanf(fp, "%hd", &(side->hitstats[u][u2]));
}
fscanf(fp, "\n");
}
}
/* Read about what has been seen in the world. */
read_side_view(side, fp)
Side *side;
FILE *fp;
{
char ch1, ch2;
int x, y, view;
for (y = 0; y < world.height; ++y) {
for (x = 0; x < world.width; ++x) {
ch1 = getc(fp); ch2 = getc(fp);
if (ch1 == '?' && ch2 == '?')
view = UNSEEN;
else if (ch1 == '.' && ch2 == '.')
view = EMPTY;
else
view = buildview(ch2 - '0', find_unit_char(ch1));
set_side_view(side, x, y, view);
}
fscanf(fp, "\n");
}
}
/* Read the entire units section. Units also have different levels of */
/* detail. */
read_units(fp)
FILE *fp;
{
int numtoread, detail, extension, i;
Unit *unit, *transport;
fscanf(fp, "Units %d %d %d\n", &numtoread, &detail, &extension);
if (detail < 0 || detail > UNITALL) case_panic("detail", detail);
if (Debug) printf("Will try to restore %d units at detail %d ...\n",
numtoread, detail);
for (i = 0; i < numtoread; ++i) {
nut = -1;
if (detail >= UNITBASIC) {
if ((unit = read_basic_unit(fp)) != NULL) {
if (detail >= UNITSLOTS) read_unit_attributes(unit, fp);
if (detail >= UNITORDERS) read_unit_orders(unit, fp);
if (nusn >= 0) {
unit_changes_side(unit, side_n(nusn), -1, -1);
}
if (nut >= 0) {
transport = find_unit(nut);
if (transport != NULL) {
occupy_unit(unit, transport);
} else {
occupy_hex(unit, nux, nuy);
}
} else {
occupy_hex(unit, nux, nuy);
}
if (Debug) printf("Got %s,\n", unit_handle(NULL, unit));
}
}
}
if (Debug) printf("... Done reading units.\n");
return detail;
}
/* Read the barest info about a neutral unit - just type, name, and loc. */
/* Do weird stuff to handle empty names represented by "*". */
/* Give it full supplies (good idea?). Can't place just yet, because it */
/* might actually be an occupant of something else. */
Unit *
read_basic_unit(fp)
FILE *fp;
{
char ch;
int j, u;
Unit *newunit;
fscanf(fp, "%c %s %d,%d %d\n", &ch, tmpbuf, &nux, &nuy, &nusn);
for (j = 0; j < strlen(tmpbuf); ++j) if (tmpbuf[j] == '*') tmpbuf[j] = ' ';
if (strcmp(tmpbuf, " ") == 0) strcpy(tmpbuf, "");
if ((u = find_unit_char(ch)) != NOTHING) {
if ((newunit = create_unit(u, tmpbuf)) != NULL) {
init_supply(newunit);
}
} else {
fprintf(stderr, "Unit '%c' is not of this period!\n", ch);
exit(1);
}
return newunit;
}
/* Read most of a unit's attributes, but not orders and suchlike. */
read_unit_attributes(unit, fp)
Unit *unit;
FILE *fp;
{
int truesidenum, r;
fscanf(fp, "%hd %hd %d %hd %hd %hd %hd %hd %hd %hd %d",
&(unit->id), &(unit->number), &truesidenum,
&(unit->hp), &(unit->quality), &(unit->morale), &(unit->fatigue),
&(unit->product), &(unit->schedule), &(unit->built), &nut);
for_all_resource_types(r) {
fscanf(fp, "%hd", &(unit->supply[r]));
}
fscanf(fp, "\n");
unit->trueside = side_n(truesidenum);
/* tricky way to ensure subsequent units will have unique ids */
nextid = max(nextid, unit->id + 1);
}
/* Read everything back in and try to recreate the unit's orders exactly. */
/* The saved orders include a flag for the presence of standing orders. */
read_unit_orders(unit, fp)
Unit *unit;
FILE *fp;
{
int i, more, uord[MAXUTYPES];
fscanf(fp, "%hd %hd", &(unit->lastdir), &(unit->awake));
read_one_order(fp, &(unit->orders));
fscanf(fp, "%d\n", &more);
if (more) {
unit->standing = (StandingOrder *) malloc(sizeof(StandingOrder));
for_all_unit_types(i) {
fscanf(fp, "%d", &(uord[i]));
}
fscanf(fp, "\n");
for_all_unit_types(i) {
if (uord[i]) {
unit->standing->orders[i] = (Order *) malloc(sizeof(Order));
read_one_order(fp, unit->standing->orders[i]);
fscanf(fp, "\n");
}
}
}
}
/* The exact way to read an order depends on what type it is. */
read_one_order(fp, order)
FILE *fp;
Order *order;
{
int leadernum;
fscanf(fp, "%hd %hd %hd", &(order->type), &(order->rept), &(order->flags));
switch (orderargs[order->type]) {
case NOARG:
break;
case DIR:
fscanf(fp, "%hd", &(order->p.dir));
break;
case POS:
fscanf(fp, "%hd,%hd", &(order->p.pt[0].x), &(order->p.pt[0].y));
break;
case LEADER:
fscanf(fp, "%d", &leadernum);
/* should finish this */
break;
case WAYPOINTS:
fscanf(fp, "%hd,%hd %hd,%hd",
&(order->p.pt[0].x), &(order->p.pt[0].y),
&(order->p.pt[1].x), &(order->p.pt[1].y));
break;
default:
case_panic("order arg type", orderargs[order->type]);
break;
}
}
/* Output is quite similar to input of mapfiles, but of course everything */
/* is reversed. */
/* A savefile has a particular format; it includes all sections except */
/* period, which may or may not be referenced as an included file. */
/* It is important that this routine not attempt to use graphics, since it */
/* may be called when graphics code fails. */
write_savefile(fname)
char *fname;
{
Header *head;
head = (Header *) malloc(sizeof(Header));
head->numincls = (perfilename ? 1 : 0);
head->includes[0] = perfilename;
head->sections = "+-++++";
head->numnotes = 0;
head->sdetail = SIDEALL;
head->udetail = UNITALL;
return write_mapfile(fname, head);
}
/* A scenario is considerably more variable than a savefile, but the */
/* principle is the same. */
write_scenario(fname, sections, sdetail, udetail)
char *fname, *sections;
int sdetail, udetail;
{
int i;
Header *head;
head = (Header *) malloc(sizeof(Header));
head->numincls = head->numnotes = 0;
if (mainhead) {
head->numnotes = mainhead->numnotes;
for (i = 0; i < head->numnotes; ++i) {
head->notes[i] = mainhead->notes[i];
}
head->numincls = mainhead->numincls;
for (i = 0; i < head->numincls; ++i) {
head->includes[i] = mainhead->includes[i];
}
}
head->sections = copy_string(sections);
head->sdetail = sdetail;
head->udetail = udetail;
return write_mapfile(fname, head);
}
/* Given a file name and a header telling what to put in, do the putting. */
/* Returns true if file opened successfully. */
write_mapfile(fname, head)
char *fname;
Header *head;
{
int i;
FILE *fp;
if ((fp = fopen(fname, "w")) != NULL) {
fprintf(fp, "Xconq %d %s %s%s\n",
head->numincls, head->sections,
(head->numnotes > 0 ? head->notes[0] : ""),
(head->numnotes > 1 ? ";" : ""));
if (head->numnotes > 1) {
for (i = 1; i < head->numnotes; ++i) {
fprintf(fp, "%s\n", head->notes[i]);
}
fprintf(fp, ".\n");
}
for (i = 0; i < head->numincls; ++i) {
fprintf(fp, "%s\n", head->includes[i]);
}
if (vsect(head->sections)) write_version(fp);
/* no period writing */
if (msect(head->sections)) write_map(fp);
if (gsect(head->sections)) write_globals(fp);
if (ssect(head->sections)) write_sides(fp, head->sdetail);
if (usect(head->sections)) write_units(fp, head->udetail);
fclose(fp);
return TRUE;
} else {
return FALSE;
}
}
/* Writing out the version is pretty easy. */
write_version(fp)
FILE *fp;
{
fprintf(fp, "%s\n", version);
}
/* Write the map section. Try to find runs of the same type and make a more */
/* compact output. */
write_map(fp)
FILE *fp;
{
int extension = 0, x, y, run, runterr, terr, i;
fprintf(fp, "Map %d %d %d %d %d\n",
world.width, world.height, world.scale, world.known, extension);
for (y = world.height-1; y >= 0; --y) {
run = 0;
runterr = terrain_at(0, y);
for (x = 0; x < world.width; ++x) {
terr = terrain_at(x, y);
if (terr == runterr) {
run++;
} else {
if (run >= 3) {
fprintf(fp, "%d%c", run, ttypes[runterr].tchar);
} else {
for (i = 0; i < run; ++i) putc(ttypes[runterr].tchar, fp);
}
runterr = terr;
run = 1;
}
}
fprintf(fp, "%d%c\n", run, ttypes[terr].tchar);
}
}
/* Write the globals section, which mostly consists of win/lose conditions. */
write_globals(fp)
FILE *fp;
{
int extension = 0, i, u, r;
Condition *cond;
fprintf(fp, "Globals %d %d %d %d %d %d\n",
global.time, global.endtime, global.setproduct,
global.leavemap, global.numconds, extension);
for (i = 0; i < global.numconds; ++i) {
cond = &(global.winlose[i]);
fprintf(fp, "%d %d %d %d %d\n",
cond->win, cond->type,
cond->starttime, cond->endtime, cond->sidesn);
switch (cond->type) {
case TERRITORY:
fprintf(fp, "%d\n", cond->n);
break;
case UNITCOUNT:
for_all_unit_types(u) {
fprintf(fp, "%d ", cond->units[u]);
}
fprintf(fp, "\n");
break;
case RESOURCECOUNT:
for_all_resource_types(r) {
fprintf(fp, "%d ", cond->resources[r]);
}
fprintf(fp, "\n");
break;
case POSSESSION:
fprintf(fp, "%d %d %d\n", cond->x, cond->y, cond->n);
break;
default:
case_panic("condition type", cond->type);
break;
}
}
}
/* Write the sides section, at the given level of detail. */
write_sides(fp, detail)
FILE *fp;
int detail;
{
int extension = 0;
Side *side;
fprintf(fp, "Sides %d %d %d\n", numsides, detail, extension);
if (detail < 0 || detail > SIDEALL) case_panic("detail", detail);
if (Debug) printf("Will try to write %d sides at detail %d ...\n",
numsides, detail);
for_all_sides(side) {
if (detail >= SIDEBASIC) write_basic_side(side, fp);
if (detail >= SIDESLOTS) write_side_attributes(side, fp);
if (detail >= SIDEVIEW) write_side_view(side, fp);
if (detail >= SIDEMISC) {
write_side_misc(side, fp);
write_side_statistics(side, fp);
}
if (Debug) printf("Wrote side \"%s\",\n", side->name);
}
if (Debug) printf("... Done writing sides.\n");
}
/* A side can be saved with or without the entire view, which is a fairly */
/* large sort of thing. Machine sides without displays have their names */
/* written as "*" - let us hope and pray that such a perverted name never */
/* appears as a valid X host/display name. */
write_basic_side(side, fp)
Side *side;
FILE *fp;
{
int j;
strcpy(tmpbuf, side->name);
for (j = 0; j < strlen(tmpbuf); ++j) if (tmpbuf[j] == ' ') tmpbuf[j] = '*';
fprintf(fp, "%s\n", tmpbuf);
}
/* Write the random but important attributes of a side. */
write_side_attributes(side, fp)
Side *side;
FILE *fp;
{
int u, s;
fprintf(fp, "%d ", side->ideology);
for (s = 0; s < numsides; ++s) fprintf(fp, "%d ", side->attitude[s]);
fprintf(fp, " ");
for_all_unit_types(u) fprintf(fp, "%d ", side->counts[u]);
fprintf(fp, "\n");
}
/* Write about what has been seen in the world. (should be more compact) */
write_side_view(side, fp)
Side *side;
FILE *fp;
{
char ch1, ch2;
int x, y, view;
for (y = 0; y < world.height; ++y) {
for (x = 0; x < world.width; ++x) {
view = side_view(side, x, y);
if (view == UNSEEN)
ch1 = ch2 = '?';
else if (view == EMPTY)
ch1 = ch2 = '.';
else {
ch1 = utypes[vtype(view)].uchar;
ch2 = vside(view) + '0';
}
putc(ch1, fp); putc(ch2, fp);
}
fprintf(fp, "\n");
}
}
/* More volatile things, generally only of interest for saved games. */
write_side_misc(side, fp)
Side *side;
FILE *fp;
{
fprintf(fp, "%d %s %d \n",
side->humanp, (side->host ? side->host : "*"),
side->lost, side->timeleft);
}
/* Write the performance statistics of a side. (the unit record may be */
/* crucial to deciding win/lose conditions.) */
write_side_statistics(side, fp)
Side *side;
FILE *fp;
{
int u, u2, i;
for_all_unit_types(u) {
for (i = 0; i < NUMREASONS; ++i) {
fprintf(fp, "%d ", side->balance[u][i]);
}
fprintf(fp, "\n");
for_all_unit_types(u2) {
fprintf(fp, "%d ", side->atkstats[u][u2]);
}
fprintf(fp, "\n");
for_all_unit_types(u2) {
fprintf(fp, "%d ", side->hitstats[u][u2]);
}
fprintf(fp, "\n");
}
}
/* Write the unit section of a mapfile. Each level of detail has its own */
/* line, while standing orders will take one line per unit type. */
/* Get rid of dead units and sort everything, so as to make sure that */
/* transports are always written before occupants. */
write_units(fp, detail)
FILE *fp;
int detail;
{
int extension = 0;
Unit *unit;
flush_dead_units();
sort_units(TRUE);
if (detail < 0 || detail > UNITALL) case_panic("detail", detail);
fprintf(fp, "Units %d %d %d\n", numunits, detail, extension);
if (Debug) printf("Writing %d units at detail %d ...\n", numunits, detail);
if (detail >= UNITBASIC) {
for_all_units(unit) {
if (detail >= UNITBASIC) write_basic_unit(unit, fp);
if (detail >= UNITSLOTS) write_unit_attributes(unit, fp);
if (detail >= UNITORDERS) write_unit_orders(unit, fp);
if (Debug) printf("Wrote %s,\n", unit_handle(NULL, unit));
}
}
if (Debug) printf("... Done writing units.\n");
}
/* Write only the minimal info about a unit - type, name, and position. */
/* To make scanf happy, spaces in the name are replaced with stars. */
/* Fortunately, names never seem to have stars in them. */
/* Unnamed units get a star all by itself in the name position. */
write_basic_unit(unit, fp)
Unit *unit;
FILE *fp;
{
int j;
if (unit->name == NULL || strlen(unit->name) < 1) {
sprintf(tmpbuf, "*");
} else {
strcpy(tmpbuf, unit->name);
for (j = 0; j < strlen(tmpbuf); ++j)
if (tmpbuf[j] == ' ') tmpbuf[j] = '*';
}
fprintf(fp, "%c %s %d,%d %d\n",
utypes[unit->type].uchar, tmpbuf, unit->x, unit->y,
side_number(unit->side));
}
/* Write the most interesting attributes of a unit. Just a long list of */
/* numbers, no special tricks needed. */
write_unit_attributes(unit, fp)
Unit *unit;
FILE *fp;
{
int i;
fprintf(fp, "%d %d %d %d %d %d %d %d %d %d %d ",
unit->id, unit->number, side_number(unit->trueside),
unit->hp, unit->quality, unit->morale, unit->fatigue,
unit->product, unit->schedule, unit->built,
(unit->transport ? unit->transport->id : -1));
for_all_resource_types(i) {
fprintf(fp, "%d ", unit->supply[i]);
}
fprintf(fp, "\n");
}
/* Write the unit's orders and standing orders out. */
/* This is usually for game saving, although I suppose it has other uses. */
write_unit_orders(unit, fp)
Unit *unit;
FILE *fp;
{
int i;
fprintf(fp, "%d %d ", unit->lastdir, unit->awake);
write_one_order(fp, &(unit->orders));
fprintf(fp, " %d\n", (unit->standing != NULL));
if (unit->standing != NULL) {
for_all_unit_types(i) {
fprintf(fp, "%d ", (unit->standing->orders[i] != NULL));
}
fprintf(fp, "\n");
for_all_unit_types(i) {
if (unit->standing->orders[i] != NULL) {
write_one_order(fp, unit->standing->orders[i]);
fprintf(fp, "\n");
}
}
}
}
/* Write a single order object, which may be for a unit or a standing order. */
write_one_order(fp, order)
FILE *fp;
Order *order;
{
fprintf(fp, " %d %d %d ", order->type, order->rept, order->flags);
switch (orderargs[order->type]) {
case NOARG:
break;
case DIR:
fprintf(fp, "%d", order->p.dir);
break;
case POS:
fprintf(fp, "%d,%d", order->p.pt[0].x, order->p.pt[0].y);
break;
case LEADER:
fprintf(fp, "%d", order->p.leader->id);
break;
case WAYPOINTS:
fprintf(fp, "%d,%d %d,%d",
order->p.pt[0].x, order->p.pt[0].y,
order->p.pt[1].x, order->p.pt[1].y);
break;
default:
case_panic("order arg type", orderargs[order->type]);
break;
}
}
/* Display the mapfile header info. */
describe_mapfiles(side)
Side *side;
{
int i;
wprintf(side, "The world is %d hexes around by %d hexes high.",
world.width, world.height);
wprintf(side, "");
if (mainhead != NULL && mainhead->numnotes > 0) {
for (i = 0; i < mainhead->numnotes; ++i) {
wprintf(side, "%s", mainhead->notes[i]);
}
} else {
wprintf(side, "(No other documentation is available.)");
}
}
/* Generalized area search routine. It starts in the immediately adjacent */
/* hexes and expands outwards. The basic structure is to examine successive */
/* "rings" out to the max distance; within each ring, we must scan each of */
/* six faces (picking a random one to start with) by iterating along that */
/* face, in a direction 120 degrees from the direction out to one corner of */
/* the face. Draw a picture if you want to understand it... */
/* Note that points far outside the map may be generated, but the predicate */
/* will not be called on them. It may be applied to the same point several */
/* times, however. */
search_area(x0, y0, maxdist, pred, rxp, ryp)
int x0, y0, maxdist, (*pred)(), *rxp, *ryp;
{
int clockwise, dist, dd, d, dir, x1, y1, i, dir2, x, y;
maxdist = max(min(maxdist, world.width), min(maxdist, world.height));
clockwise = (flip_coin() ? 1 : -1);
for (dist = 1; dist <= maxdist; ++dist) {
dd = random_dir();
for_all_directions(d) {
dir = (d + dd) % NUMDIRS;
x1 = x0 + dist * dirx[dir];
y1 = y0 + dist * diry[dir];
for (i = 0; i < dist; ++i) {
dir2 = opposite_dir(dir + clockwise);
x = x1 + i * dirx[dir2];
y = y1 + i * diry[dir2];
if (between(0, y, world.height-1)) {
if ((*pred)(wrap(x), y)) {
*rxp = wrap(x); *ryp = y;
return TRUE;
}
}
}
}
}
return FALSE;
}
/* Apply a function to every hex within the given radius, being careful (for */
/* both safety and efficiency reasons) not to go past edges. Note that the */
/* distance is inclusive, and that distance of 0 means x0,y0 only. */
/* This routine should be avoided in time-critical code. */
apply_to_area(x0, y0, dist, fn)
int x0, y0, dist, (*fn)();
{
int x, y, x1, y1, x2, y2;
dist = min(dist, max(world.width, world.height));
y1 = y0 - dist;
y2 = y0 + dist;
for (y = y1; y <= y2; ++y) {
if (between(0, y, world.height-1)) {
x1 = x0 - (y < y0 ? (y - y1) : dist);
x2 = x0 + (y > y0 ? (y2 - y) : dist);
for (x = x1; x <= x2; ++x) {
((*fn)(wrap(x), y));
}
}
}
}
#ifdef SCO_UNIX
int
windex(x,y)
{
return (world.width * y + x);
}
#endif